/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.security.cert;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Parameters used as input for the PKIX {@code CertPathValidator}
 * algorithm.
 * <p>
 * A PKIX {@code CertPathValidator} uses these parameters to
 * validate a {@code CertPath} according to the PKIX certification path
 * validation algorithm.
 *
 * <p>To instantiate a {@code PKIXParameters} object, an
 * application must specify one or more <i>most-trusted CAs</i> as defined by
 * the PKIX certification path validation algorithm. The most-trusted CAs
 * can be specified using one of two constructors. An application
 * can call {@link #PKIXParameters(Set) PKIXParameters(Set)},
 * specifying a {@code Set} of {@code TrustAnchor} objects, each
 * of which identify a most-trusted CA. Alternatively, an application can call
 * {@link #PKIXParameters(KeyStore) PKIXParameters(KeyStore)}, specifying a
 * {@code KeyStore} instance containing trusted certificate entries, each
 * of which will be considered as a most-trusted CA.
 * <p>
 * Once a {@code PKIXParameters} object has been created, other parameters
 * can be specified (by calling {@link #setInitialPolicies setInitialPolicies}
 * or {@link #setDate setDate}, for instance) and then the
 * {@code PKIXParameters} is passed along with the {@code CertPath}
 * to be validated to {@link CertPathValidator#validate
 * CertPathValidator.validate}.
 * <p>
 * Any parameter that is not set (or is set to {@code null}) will
 * be set to the default value for that parameter. The default value for the
 * {@code date} parameter is {@code null}, which indicates
 * the current time when the path is validated. The default for the
 * remaining parameters is the least constrained.
 * <p>
 * <b>Concurrent Access</b>
 * <p>
 * Unless otherwise specified, the methods defined in this class are not
 * thread-safe. Multiple threads that need to access a single
 * object concurrently should synchronize amongst themselves and
 * provide the necessary locking. Multiple threads each manipulating
 * separate objects need not synchronize.
 *
 * @author Sean Mullan
 * @author Yassir Elley
 * @see CertPathValidator
 * @since 1.4
 */
public class PKIXParameters implements CertPathParameters {

  private Set<TrustAnchor> unmodTrustAnchors;
  private Date date;
  private List<PKIXCertPathChecker> certPathCheckers;
  private String sigProvider;
  private boolean revocationEnabled = true;
  private Set<String> unmodInitialPolicies;
  private boolean explicitPolicyRequired = false;
  private boolean policyMappingInhibited = false;
  private boolean anyPolicyInhibited = false;
  private boolean policyQualifiersRejected = true;
  private List<CertStore> certStores;
  private CertSelector certSelector;

  /**
   * Creates an instance of {@code PKIXParameters} with the specified
   * {@code Set} of most-trusted CAs. Each element of the
   * set is a {@link TrustAnchor TrustAnchor}.
   * <p>
   * Note that the {@code Set} is copied to protect against
   * subsequent modifications.
   *
   * @param trustAnchors a {@code Set} of {@code TrustAnchor}s
   * @throws InvalidAlgorithmParameterException if the specified {@code Set} is empty {@code
   * (trustAnchors.isEmpty() == true)}
   * @throws NullPointerException if the specified {@code Set} is {@code null}
   * @throws ClassCastException if any of the elements in the {@code Set} are not of type {@code
   * java.security.cert.TrustAnchor}
   */
  public PKIXParameters(Set<TrustAnchor> trustAnchors)
      throws InvalidAlgorithmParameterException {
    setTrustAnchors(trustAnchors);

    this.unmodInitialPolicies = Collections.<String>emptySet();
    this.certPathCheckers = new ArrayList<PKIXCertPathChecker>();
    this.certStores = new ArrayList<CertStore>();
  }

  /**
   * Creates an instance of {@code PKIXParameters} that
   * populates the set of most-trusted CAs from the trusted
   * certificate entries contained in the specified {@code KeyStore}.
   * Only keystore entries that contain trusted {@code X509Certificates}
   * are considered; all other certificate types are ignored.
   *
   * @param keystore a {@code KeyStore} from which the set of most-trusted CAs will be populated
   * @throws KeyStoreException if the keystore has not been initialized
   * @throws InvalidAlgorithmParameterException if the keystore does not contain at least one
   * trusted certificate entry
   * @throws NullPointerException if the keystore is {@code null}
   */
  public PKIXParameters(KeyStore keystore)
      throws KeyStoreException, InvalidAlgorithmParameterException {
    if (keystore == null) {
      throw new NullPointerException("the keystore parameter must be " +
          "non-null");
    }
    Set<TrustAnchor> hashSet = new HashSet<TrustAnchor>();
    Enumeration<String> aliases = keystore.aliases();
    while (aliases.hasMoreElements()) {
      String alias = aliases.nextElement();
      if (keystore.isCertificateEntry(alias)) {
        Certificate cert = keystore.getCertificate(alias);
        if (cert instanceof X509Certificate) {
          hashSet.add(new TrustAnchor((X509Certificate) cert, null));
        }
      }
    }
    setTrustAnchors(hashSet);
    this.unmodInitialPolicies = Collections.<String>emptySet();
    this.certPathCheckers = new ArrayList<PKIXCertPathChecker>();
    this.certStores = new ArrayList<CertStore>();
  }

  /**
   * Returns an immutable {@code Set} of the most-trusted
   * CAs.
   *
   * @return an immutable {@code Set} of {@code TrustAnchor}s (never {@code null})
   * @see #setTrustAnchors
   */
  public Set<TrustAnchor> getTrustAnchors() {
    return this.unmodTrustAnchors;
  }

  /**
   * Sets the {@code Set} of most-trusted CAs.
   * <p>
   * Note that the {@code Set} is copied to protect against
   * subsequent modifications.
   *
   * @param trustAnchors a {@code Set} of {@code TrustAnchor}s
   * @throws InvalidAlgorithmParameterException if the specified {@code Set} is empty {@code
   * (trustAnchors.isEmpty() == true)}
   * @throws NullPointerException if the specified {@code Set} is {@code null}
   * @throws ClassCastException if any of the elements in the set are not of type {@code
   * java.security.cert.TrustAnchor}
   * @see #getTrustAnchors
   */
  public void setTrustAnchors(Set<TrustAnchor> trustAnchors)
      throws InvalidAlgorithmParameterException {
    if (trustAnchors == null) {
      throw new NullPointerException("the trustAnchors parameters must" +
          " be non-null");
    }
    if (trustAnchors.isEmpty()) {
      throw new InvalidAlgorithmParameterException("the trustAnchors " +
          "parameter must be non-empty");
    }
    for (Iterator<TrustAnchor> i = trustAnchors.iterator(); i.hasNext(); ) {
      if (!(i.next() instanceof TrustAnchor)) {
        throw new ClassCastException("all elements of set must be "
            + "of type java.security.cert.TrustAnchor");
      }
    }
    this.unmodTrustAnchors = Collections.unmodifiableSet
        (new HashSet<TrustAnchor>(trustAnchors));
  }

  /**
   * Returns an immutable {@code Set} of initial
   * policy identifiers (OID strings), indicating that any one of these
   * policies would be acceptable to the certificate user for the purposes of
   * certification path processing. The default return value is an empty
   * {@code Set}, which is interpreted as meaning that any policy would
   * be acceptable.
   *
   * @return an immutable {@code Set} of initial policy OIDs in {@code String} format, or an empty
   * {@code Set} (implying any policy is acceptable). Never returns {@code null}.
   * @see #setInitialPolicies
   */
  public Set<String> getInitialPolicies() {
    return this.unmodInitialPolicies;
  }

  /**
   * Sets the {@code Set} of initial policy identifiers
   * (OID strings), indicating that any one of these
   * policies would be acceptable to the certificate user for the purposes of
   * certification path processing. By default, any policy is acceptable
   * (i.e. all policies), so a user that wants to allow any policy as
   * acceptable does not need to call this method, or can call it
   * with an empty {@code Set} (or {@code null}).
   * <p>
   * Note that the {@code Set} is copied to protect against
   * subsequent modifications.
   *
   * @param initialPolicies a {@code Set} of initial policy OIDs in {@code String} format (or {@code
   * null})
   * @throws ClassCastException if any of the elements in the set are not of type {@code String}
   * @see #getInitialPolicies
   */
  public void setInitialPolicies(Set<String> initialPolicies) {
    if (initialPolicies != null) {
      for (Iterator<String> i = initialPolicies.iterator();
          i.hasNext(); ) {
        if (!(i.next() instanceof String)) {
          throw new ClassCastException("all elements of set must be "
              + "of type java.lang.String");
        }
      }
      this.unmodInitialPolicies =
          Collections.unmodifiableSet(new HashSet<String>(initialPolicies));
    } else {
      this.unmodInitialPolicies = Collections.<String>emptySet();
    }
  }

  /**
   * Sets the list of {@code CertStore}s to be used in finding
   * certificates and CRLs. May be {@code null}, in which case
   * no {@code CertStore}s will be used. The first
   * {@code CertStore}s in the list may be preferred to those that
   * appear later.
   * <p>
   * Note that the {@code List} is copied to protect against
   * subsequent modifications.
   *
   * @param stores a {@code List} of {@code CertStore}s (or {@code null})
   * @throws ClassCastException if any of the elements in the list are not of type {@code
   * java.security.cert.CertStore}
   * @see #getCertStores
   */
  public void setCertStores(List<CertStore> stores) {
    if (stores == null) {
      this.certStores = new ArrayList<CertStore>();
    } else {
      for (Iterator<CertStore> i = stores.iterator(); i.hasNext(); ) {
        if (!(i.next() instanceof CertStore)) {
          throw new ClassCastException("all elements of list must be "
              + "of type java.security.cert.CertStore");
        }
      }
      this.certStores = new ArrayList<CertStore>(stores);
    }
  }

  /**
   * Adds a {@code CertStore} to the end of the list of
   * {@code CertStore}s used in finding certificates and CRLs.
   *
   * @param store the {@code CertStore} to add. If {@code null}, the store is ignored (not added to
   * list).
   */
  public void addCertStore(CertStore store) {
    if (store != null) {
      this.certStores.add(store);
    }
  }

  /**
   * Returns an immutable {@code List} of {@code CertStore}s that
   * are used to find certificates and CRLs.
   *
   * @return an immutable {@code List} of {@code CertStore}s (may be empty, but never {@code null})
   * @see #setCertStores
   */
  public List<CertStore> getCertStores() {
    return Collections.unmodifiableList
        (new ArrayList<CertStore>(this.certStores));
  }

  /**
   * Sets the RevocationEnabled flag. If this flag is true, the default
   * revocation checking mechanism of the underlying PKIX service provider
   * will be used. If this flag is false, the default revocation checking
   * mechanism will be disabled (not used).
   * <p>
   * When a {@code PKIXParameters} object is created, this flag is set
   * to true. This setting reflects the most common strategy for checking
   * revocation, since each service provider must support revocation
   * checking to be PKIX compliant. Sophisticated applications should set
   * this flag to false when it is not practical to use a PKIX service
   * provider's default revocation checking mechanism or when an alternative
   * revocation checking mechanism is to be substituted (by also calling the
   * {@link #addCertPathChecker addCertPathChecker} or {@link
   * #setCertPathCheckers setCertPathCheckers} methods).
   *
   * @param val the new value of the RevocationEnabled flag
   */
  public void setRevocationEnabled(boolean val) {
    revocationEnabled = val;
  }

  /**
   * Checks the RevocationEnabled flag. If this flag is true, the default
   * revocation checking mechanism of the underlying PKIX service provider
   * will be used. If this flag is false, the default revocation checking
   * mechanism will be disabled (not used). See the {@link
   * #setRevocationEnabled setRevocationEnabled} method for more details on
   * setting the value of this flag.
   *
   * @return the current value of the RevocationEnabled flag
   */
  public boolean isRevocationEnabled() {
    return revocationEnabled;
  }

  /**
   * Sets the ExplicitPolicyRequired flag. If this flag is true, an
   * acceptable policy needs to be explicitly identified in every certificate.
   * By default, the ExplicitPolicyRequired flag is false.
   *
   * @param val {@code true} if explicit policy is to be required, {@code false} otherwise
   */
  public void setExplicitPolicyRequired(boolean val) {
    explicitPolicyRequired = val;
  }

  /**
   * Checks if explicit policy is required. If this flag is true, an
   * acceptable policy needs to be explicitly identified in every certificate.
   * By default, the ExplicitPolicyRequired flag is false.
   *
   * @return {@code true} if explicit policy is required, {@code false} otherwise
   */
  public boolean isExplicitPolicyRequired() {
    return explicitPolicyRequired;
  }

  /**
   * Sets the PolicyMappingInhibited flag. If this flag is true, policy
   * mapping is inhibited. By default, policy mapping is not inhibited (the
   * flag is false).
   *
   * @param val {@code true} if policy mapping is to be inhibited, {@code false} otherwise
   */
  public void setPolicyMappingInhibited(boolean val) {
    policyMappingInhibited = val;
  }

  /**
   * Checks if policy mapping is inhibited. If this flag is true, policy
   * mapping is inhibited. By default, policy mapping is not inhibited (the
   * flag is false).
   *
   * @return true if policy mapping is inhibited, false otherwise
   */
  public boolean isPolicyMappingInhibited() {
    return policyMappingInhibited;
  }

  /**
   * Sets state to determine if the any policy OID should be processed
   * if it is included in a certificate. By default, the any policy OID
   * is not inhibited ({@link #isAnyPolicyInhibited isAnyPolicyInhibited()}
   * returns {@code false}).
   *
   * @param val {@code true} if the any policy OID is to be inhibited, {@code false} otherwise
   */
  public void setAnyPolicyInhibited(boolean val) {
    anyPolicyInhibited = val;
  }

  /**
   * Checks whether the any policy OID should be processed if it
   * is included in a certificate.
   *
   * @return {@code true} if the any policy OID is inhibited, {@code false} otherwise
   */
  public boolean isAnyPolicyInhibited() {
    return anyPolicyInhibited;
  }

  /**
   * Sets the PolicyQualifiersRejected flag. If this flag is true,
   * certificates that include policy qualifiers in a certificate
   * policies extension that is marked critical are rejected.
   * If the flag is false, certificates are not rejected on this basis.
   *
   * <p> When a {@code PKIXParameters} object is created, this flag is
   * set to true. This setting reflects the most common (and simplest)
   * strategy for processing policy qualifiers. Applications that want to use
   * a more sophisticated policy must set this flag to false.
   * <p>
   * Note that the PKIX certification path validation algorithm specifies
   * that any policy qualifier in a certificate policies extension that is
   * marked critical must be processed and validated. Otherwise the
   * certification path must be rejected. If the policyQualifiersRejected flag
   * is set to false, it is up to the application to validate all policy
   * qualifiers in this manner in order to be PKIX compliant.
   *
   * @param qualifiersRejected the new value of the PolicyQualifiersRejected flag
   * @see #getPolicyQualifiersRejected
   * @see PolicyQualifierInfo
   */
  public void setPolicyQualifiersRejected(boolean qualifiersRejected) {
    policyQualifiersRejected = qualifiersRejected;
  }

  /**
   * Gets the PolicyQualifiersRejected flag. If this flag is true,
   * certificates that include policy qualifiers in a certificate policies
   * extension that is marked critical are rejected.
   * If the flag is false, certificates are not rejected on this basis.
   *
   * <p> When a {@code PKIXParameters} object is created, this flag is
   * set to true. This setting reflects the most common (and simplest)
   * strategy for processing policy qualifiers. Applications that want to use
   * a more sophisticated policy must set this flag to false.
   *
   * @return the current value of the PolicyQualifiersRejected flag
   * @see #setPolicyQualifiersRejected
   */
  public boolean getPolicyQualifiersRejected() {
    return policyQualifiersRejected;
  }

  /**
   * Returns the time for which the validity of the certification path
   * should be determined. If {@code null}, the current time is used.
   * <p>
   * Note that the {@code Date} returned is copied to protect against
   * subsequent modifications.
   *
   * @return the {@code Date}, or {@code null} if not set
   * @see #setDate
   */
  public Date getDate() {
    if (date == null) {
      return null;
    } else {
      return (Date) this.date.clone();
    }
  }

  /**
   * Sets the time for which the validity of the certification path
   * should be determined. If {@code null}, the current time is used.
   * <p>
   * Note that the {@code Date} supplied here is copied to protect
   * against subsequent modifications.
   *
   * @param date the {@code Date}, or {@code null} for the current time
   * @see #getDate
   */
  public void setDate(Date date) {
    if (date != null) {
      this.date = (Date) date.clone();
    } else {
      date = null;
    }
  }

  /**
   * Sets a {@code List} of additional certification path checkers. If
   * the specified {@code List} contains an object that is not a
   * {@code PKIXCertPathChecker}, it is ignored.
   * <p>
   * Each {@code PKIXCertPathChecker} specified implements
   * additional checks on a certificate. Typically, these are checks to
   * process and verify private extensions contained in certificates.
   * Each {@code PKIXCertPathChecker} should be instantiated with any
   * initialization parameters needed to execute the check.
   * <p>
   * This method allows sophisticated applications to extend a PKIX
   * {@code CertPathValidator} or {@code CertPathBuilder}.
   * Each of the specified {@code PKIXCertPathChecker}s will be called,
   * in turn, by a PKIX {@code CertPathValidator} or
   * {@code CertPathBuilder} for each certificate processed or
   * validated.
   * <p>
   * Regardless of whether these additional {@code PKIXCertPathChecker}s
   * are set, a PKIX {@code CertPathValidator} or
   * {@code CertPathBuilder} must perform all of the required PKIX
   * checks on each certificate. The one exception to this rule is if the
   * RevocationEnabled flag is set to false (see the {@link
   * #setRevocationEnabled setRevocationEnabled} method).
   * <p>
   * Note that the {@code List} supplied here is copied and each
   * {@code PKIXCertPathChecker} in the list is cloned to protect
   * against subsequent modifications.
   *
   * @param checkers a {@code List} of {@code PKIXCertPathChecker}s. May be {@code null}, in which
   * case no additional checkers will be used.
   * @throws ClassCastException if any of the elements in the list are not of type {@code
   * java.security.cert.PKIXCertPathChecker}
   * @see #getCertPathCheckers
   */
  public void setCertPathCheckers(List<PKIXCertPathChecker> checkers) {
    if (checkers != null) {
      List<PKIXCertPathChecker> tmpList =
          new ArrayList<PKIXCertPathChecker>();
      for (PKIXCertPathChecker checker : checkers) {
        tmpList.add((PKIXCertPathChecker) checker.clone());
      }
      this.certPathCheckers = tmpList;
    } else {
      this.certPathCheckers = new ArrayList<PKIXCertPathChecker>();
    }
  }

  /**
   * Returns the {@code List} of certification path checkers.
   * The returned {@code List} is immutable, and each
   * {@code PKIXCertPathChecker} in the {@code List} is cloned
   * to protect against subsequent modifications.
   *
   * @return an immutable {@code List} of {@code PKIXCertPathChecker}s (may be empty, but not {@code
   * null})
   * @see #setCertPathCheckers
   */
  public List<PKIXCertPathChecker> getCertPathCheckers() {
    List<PKIXCertPathChecker> tmpList = new ArrayList<PKIXCertPathChecker>();
    for (PKIXCertPathChecker ck : certPathCheckers) {
      tmpList.add((PKIXCertPathChecker) ck.clone());
    }
    return Collections.unmodifiableList(tmpList);
  }

  /**
   * Adds a {@code PKIXCertPathChecker} to the list of certification
   * path checkers. See the {@link #setCertPathCheckers setCertPathCheckers}
   * method for more details.
   * <p>
   * Note that the {@code PKIXCertPathChecker} is cloned to protect
   * against subsequent modifications.
   *
   * @param checker a {@code PKIXCertPathChecker} to add to the list of checks. If {@code null}, the
   * checker is ignored (not added to list).
   */
  public void addCertPathChecker(PKIXCertPathChecker checker) {
    if (checker != null) {
      certPathCheckers.add((PKIXCertPathChecker) checker.clone());
    }
  }

  /**
   * Returns the signature provider's name, or {@code null}
   * if not set.
   *
   * @return the signature provider's name (or {@code null})
   * @see #setSigProvider
   */
  public String getSigProvider() {
    return this.sigProvider;
  }

  /**
   * Sets the signature provider's name. The specified provider will be
   * preferred when creating {@link java.security.Signature Signature}
   * objects. If {@code null} or not set, the first provider found
   * supporting the algorithm will be used.
   *
   * @param sigProvider the signature provider's name (or {@code null})
   * @see #getSigProvider
   */
  public void setSigProvider(String sigProvider) {
    this.sigProvider = sigProvider;
  }

  /**
   * Returns the required constraints on the target certificate.
   * The constraints are returned as an instance of {@code CertSelector}.
   * If {@code null}, no constraints are defined.
   *
   * <p>Note that the {@code CertSelector} returned is cloned
   * to protect against subsequent modifications.
   *
   * @return a {@code CertSelector} specifying the constraints on the target certificate (or {@code
   * null})
   * @see #setTargetCertConstraints
   */
  public CertSelector getTargetCertConstraints() {
    if (certSelector != null) {
      return (CertSelector) certSelector.clone();
    } else {
      return null;
    }
  }

  /**
   * Sets the required constraints on the target certificate.
   * The constraints are specified as an instance of
   * {@code CertSelector}. If {@code null}, no constraints are
   * defined.
   *
   * <p>Note that the {@code CertSelector} specified is cloned
   * to protect against subsequent modifications.
   *
   * @param selector a {@code CertSelector} specifying the constraints on the target certificate (or
   * {@code null})
   * @see #getTargetCertConstraints
   */
  public void setTargetCertConstraints(CertSelector selector) {
    if (selector != null) {
      certSelector = (CertSelector) selector.clone();
    } else {
      certSelector = null;
    }
  }

  /**
   * Makes a copy of this {@code PKIXParameters} object. Changes
   * to the copy will not affect the original and vice versa.
   *
   * @return a copy of this {@code PKIXParameters} object
   */
  public Object clone() {
    try {
      PKIXParameters copy = (PKIXParameters) super.clone();

      // must clone these because addCertStore, et al. modify them
      if (certStores != null) {
        copy.certStores = new ArrayList<CertStore>(certStores);
      }
      if (certPathCheckers != null) {
        copy.certPathCheckers =
            new ArrayList<PKIXCertPathChecker>(certPathCheckers.size());
        for (PKIXCertPathChecker checker : certPathCheckers) {
          copy.certPathCheckers.add(
              (PKIXCertPathChecker) checker.clone());
        }
      }

      // other class fields are immutable to public, don't bother
      // to clone the read-only fields.
      return copy;
    } catch (CloneNotSupportedException e) {
            /* Cannot happen */
      throw new InternalError(e.toString(), e);
    }
  }

  /**
   * Returns a formatted string describing the parameters.
   *
   * @return a formatted string describing the parameters.
   */
  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append("[\n");

        /* start with trusted anchor info */
    if (unmodTrustAnchors != null) {
      sb.append("  Trust Anchors: " + unmodTrustAnchors.toString()
          + "\n");
    }

        /* now, append initial state information */
    if (unmodInitialPolicies != null) {
      if (unmodInitialPolicies.isEmpty()) {
        sb.append("  Initial Policy OIDs: any\n");
      } else {
        sb.append("  Initial Policy OIDs: ["
            + unmodInitialPolicies.toString() + "]\n");
      }
    }

        /* now, append constraints on all certificates in the path */
    sb.append("  Validity Date: " + String.valueOf(date) + "\n");
    sb.append("  Signature Provider: " + String.valueOf(sigProvider) + "\n");
    sb.append("  Default Revocation Enabled: " + revocationEnabled + "\n");
    sb.append("  Explicit Policy Required: " + explicitPolicyRequired + "\n");
    sb.append("  Policy Mapping Inhibited: " + policyMappingInhibited + "\n");
    sb.append("  Any Policy Inhibited: " + anyPolicyInhibited + "\n");
    sb.append("  Policy Qualifiers Rejected: " + policyQualifiersRejected + "\n");

        /* now, append target cert requirements */
    sb.append("  Target Cert Constraints: " + String.valueOf(certSelector) + "\n");

        /* finally, append miscellaneous parameters */
    if (certPathCheckers != null) {
      sb.append("  Certification Path Checkers: ["
          + certPathCheckers.toString() + "]\n");
    }
    if (certStores != null) {
      sb.append("  CertStores: [" + certStores.toString() + "]\n");
    }
    sb.append("]");
    return sb.toString();
  }
}
